import { check } from '../src/check';
import { IParams } from '../src/params';
import { SaeInterface } from '../src/sae';
import * as sinon from 'sinon';
import { expect } from 'chai';
import * as step from '@flow-step/step-toolkit';
import {
    DescribeChangeOrderRequest,
    DescribeChangeOrderResponse
} from '@alicloud/sae20190506';

const params: IParams = {
    PACKAGE: '',
    APPLICATION_ID: 'app123',
    Batch: '',
    BatchWaitTime: '',
    ReleaseType: '',
    StrategyType: '',
    USE_ACR_EE: false,
    buildJobID: 0,
    domestic: false,
    minReadyInstanceType: '',
    pipelineID: 0,
    pipelineName: '',
    projectDir: '',
    regionID: 'cn-hangzhou',
    workSpace: '',
    CHANGE_ORDER_ID: '12345',
    APP_NAME: 'my-app'
};

describe('check', () => {
    let mockSae: SaeInterface;
    let sandbox: sinon.SinonSandbox;
    let describeChangeOrderStub: sinon.SinonStub;
    let getNextPipelineIDStub: sinon.SinonStub;
    let addOutputStub: sinon.SinonStub;
    let runActionStub: sinon.SinonStub;
    let exportEnvVarStub: sinon.SinonStub;
    let infoStub: sinon.SinonStub;
    let appendActionsStub: sinon.SinonStub;

    beforeEach(() => {
        sandbox = sinon.createSandbox();
        describeChangeOrderStub = sandbox.stub();
        getNextPipelineIDStub = sandbox.stub();
        addOutputStub = sandbox.stub(step, 'addOutput');
        runActionStub = sandbox.stub(step.interaction, 'runAction');
        exportEnvVarStub = sandbox.stub(step, 'exportEnvVar');
        infoStub = sandbox.stub(step, 'info');
        appendActionsStub = sandbox.stub(step.interaction, 'appendActions');

        mockSae = {
            describeChangeOrder: describeChangeOrderStub,
            getNextPipelineID: getNextPipelineIDStub
        } as unknown as SaeInterface;

    });

    afterEach(() => {
        sandbox.restore();

    });

    it('should throw an error if CHANGE_ORDER_ID is not set', async () => {
        const paramsWithoutChangeOrderId: IParams = { ...params, CHANGE_ORDER_ID: undefined };
        try {
            await check(paramsWithoutChangeOrderId, mockSae);
            expect.fail('Expected check to throw an error');
        } catch (error: any) {
            expect(error.message).to.equal('CHANGE_ORDER_ID is not set in environment variables');
        }
    });

    it('should handle successful deployment status', async () => {
        const mockResponse = new DescribeChangeOrderResponse({
            body: {
                code: '200',
                data: {
                    changeOrderId: '12345',
                    status: 2
                }
            }
        });

        describeChangeOrderStub.resolves(mockResponse);
        getNextPipelineIDStub.resolves(undefined);

        await check(params, mockSae);

        expect(describeChangeOrderStub.calledOnce).to.be.true;
        expect(describeChangeOrderStub.firstCall.args[0]).to.deep.equal(new DescribeChangeOrderRequest({ changeOrderId: '12345' }));
        expect(getNextPipelineIDStub.calledOnce).to.be.true;
        expect(exportEnvVarStub.notCalled).to.be.true;
        expect(infoStub.calledWith('[USER]The change order is executed successfully.')).to.be.true;
        expect(addOutputStub.calledWith('RERUNABLE=true')).to.be.true;
    });

    it('should handle failed deployment status', async () => {
        const mockResponse = new DescribeChangeOrderResponse({
            body: {
                code: '500',
                data: {
                    changeOrderId: '12345',
                    status: 10
                }
            }
        });

        describeChangeOrderStub.resolves(mockResponse);
        getNextPipelineIDStub.resolves(undefined);

        try {
            await check(params, mockSae);
            expect.fail('Expected check to throw an error');
        } catch (e: any) {
            expect(e.message).to.equal('check deployment failed, code: 500, message: undefined');
        }
    });

    it('should handle unknown deployment status', async () => {
        const mockResponse = new DescribeChangeOrderResponse({
            body: {
                code: '200',
                data: {
                    changeOrderId: '12345',
                    status: 99
                }
            }
        });

        describeChangeOrderStub.resolves(mockResponse);
        getNextPipelineIDStub.resolves(undefined);

        try {
            await check(params, mockSae);
            expect.fail('Expected check to throw an error');
        } catch (e: any) {
            expect(e.message).to.equal('[USER]The execution status is unknown: 99');
        }
    });

    it('should handle pending manual release batch', async () => {
        const mockResponse = new DescribeChangeOrderResponse({
            body: {
                code: '200',
                data: {
                    changeOrderId: '12345',
                    status: 8
                }
            }
        });

        describeChangeOrderStub.resolves(mockResponse);
        getNextPipelineIDStub.resolves('next-pipeline-id');

        await check(params, mockSae);

        expect(describeChangeOrderStub.calledOnce).to.be.true;
        expect(getNextPipelineIDStub.calledOnce).to.be.true;
        expect(exportEnvVarStub.calledOnceWith('NEXT_PIPELINE_ID', 'next-pipeline-id')).to.be.true;
        expect(infoStub.calledWith('[USER]The execution process is pending. You must manually determine the release batch.')).to.be.true;
        expect(addOutputStub.calledWith('NEXT_PIPELINE_ID=next-pipeline-id')).to.be.true;
        expect(appendActionsStub.calledOnce).to.be.true;
    });

    it('should handle other statuses', async () => {
        const mockResponse = new DescribeChangeOrderResponse({
            body: {
                code: '200',
                data: {
                    changeOrderId: '12345',
                    status: 1
                }
            }
        });

        describeChangeOrderStub.resolves(mockResponse);
        getNextPipelineIDStub.resolves(undefined);

        await check(params, mockSae);

        expect(describeChangeOrderStub.calledOnce).to.be.true;
        expect(infoStub.calledWith('[USER]The change order is being executed.')).to.be.true;
        expect(runActionStub.calledOnceWith('CHECK')).to.be.true;
    });

    it('should throw an error if getNextPipelineID returns undefined', async () => {
        const mockResponse = new DescribeChangeOrderResponse({
            body: {
                code: '200',
                data: {
                    changeOrderId: '12345',
                    status: 8
                }
            }
        });

        describeChangeOrderStub.resolves(mockResponse);
        getNextPipelineIDStub.resolves(undefined);

        try {
            await check(params, mockSae);
            expect.fail('Expected check to throw an error');
        } catch (e: any) {
            expect(e.message).to.equal('next pipeline id is empty');
        }
    });

    it('should handle describeChangeOrder failure', async () => {
        const error = new Error('Some error occurred');
        describeChangeOrderStub.rejects(error);

        try {
            await check(params, mockSae);
            expect.fail('Expected check to throw an error');
        } catch (e: any) {
            expect(e.message).to.equal(`failed to get application config: ${error}`);
        }
    });
});
